/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	PrintSample

		 PrintDlg.cpp

			Message handler procedure for print progress dialog box.

 ------------------------------------------------------------------

 Copyright (c) 2010 Microsoft Corporation. All rights reserved.

 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
 PARTICULAR PURPOSE.

 ------------------------------------------------------------------
*/
#include "stdafx.h"
#include "PrintSample.h"

typedef struct tagPrintDlgInfo {
	DWORD		lastPrintStatus;
	HWND		progressBarWindow;
} PRINTDLGINFO, *PPRINTDLGINFO;

/** 
	<module>PrintDlg.cpp</module>

	<summary>
		Message handler for the print progress dialog box.</summary>

	<param name="hDlg">
		Handle to the dialog box window</param>

	<param name="message">
		Window message to process.</param>

	<param name="wParam">
		Paramter passed with window message. Usage depends on value of message parameter.</param>

	<param name="lParam">
		Paramter passed with window message. Usage depends on value of message parameter.</param>

	<returns>
		<p>(INT_PTR)TRUE if this procedure handled the message and not further processing is necessary</p>
        <p>(INT_PTR)FALSE if the message was not handled and the message should be handed by the dialog manager.</p></returns>

	<remarks>
		<p>This procedure handles the following window messages.</p>
		<p>WM_INITDIALOG: initializes the controls.</p>
		<p>WM_SETCURSOR: sets the cursor to indicate whether the Cancel button 
			can be clicked.</p>
		<p>USER_PRINT_START_PRINTING: sets the progress bar parameters for the print
			job and creates the printing thread.</p>
		<p>WM_COMMAND - IDCANCEL: sets the cancel print job event.</p>
		<p>USER_PRINT_STATUS_UPDATE: updates the progress bar and status text.</p>
		<p>USER_PRINT_CLOSING: sets the closing status text below the progress bar
			to indicate that the print job is closing.</p>
		<p>USER_PRINT_COMPLETE: displays the "complete" message to the user and 
			closes and releases handles and events used to print.</p>
		</remarks>

*/
INT_PTR CALLBACK 
PrintDlgProc(
		HWND hDlg,
		UINT message,
		WPARAM wParam,
		LPARAM lParam
)
{
	INITCOMMONCONTROLSEX	icc;
	HWND					okButton = NULL;
	DWORD					waitStatus = 0L;
	PPRINTTHREADINFO		threadInfo = NULL;
	PPRINTDLGINFO			dlgInfo = NULL;

	UNREFERENCED_PARAMETER(lParam);
	
	// Get the pointer to the application's data structure.
	threadInfo = (PPRINTTHREADINFO)GetWindowLongPtr(
					GetParent(hDlg), 
					GWLP_USERDATA);

	if (NULL == threadInfo)
	{
		// There's no application data available so let the 
		//  default Dlg proc handle this message.
		return (INT_PTR)FALSE;
	}

	switch (message)
	{
	case WM_INITDIALOG:
		// Allocate the data structure for the print
		//  thread data.
		dlgInfo = (PPRINTDLGINFO)HeapAlloc (
						GetProcessHeap(), 
						PRINT_SAMPLE_HEAP_FLAGS, 
						sizeof(PRINTDLGINFO));

		if (NULL != dlgInfo)
		{
			// Save the pointer to the dialog box's data structure
			SetWindowLong(hDlg, GWLP_USERDATA, (LONG)dlgInfo);

			// Save the message to keep track of
			//  the pring job's processing.
			dlgInfo->lastPrintStatus = (DWORD)message;

			// Clear the status field.
			SetDlgItemText(hDlg, IDC_PRINT_STATUS, L"");

			// Initialize the common control interface.
			icc.dwSize = sizeof(icc);
			icc.dwICC = ICC_PROGRESS_CLASS;

			// Load the progress bar functions.
			InitCommonControlsEx(&icc);
			
			// Get window handle of progress bar
			dlgInfo->progressBarWindow = GetDlgItem(hDlg, IDC_JOB_PROGRESS);

			// Initialize the relevant application data
			threadInfo->quitEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
			threadInfo->parentWindow = hDlg;
			threadInfo->currentPage = 1;
			
			// Send the message to start printing to the printer.
			PostMessage (hDlg, USER_PRINT_START_PRINTING, 0L, 0L);

			// Indicate no further processing is required for this message.
			return (INT_PTR)TRUE;
		}
		else
		{
			// unable to allocate data for the print dialog box
			// so close the dialog box and return
			EndDialog (hDlg, E_FAIL);

			// Indicate no further processing is required for this message.
			return (INT_PTR)TRUE;
		}

	case WM_SETCURSOR:
		// Set the cursor to indicate the current status of the print job.
		//  If the user can still cancel the job, show the arrow (normal)
		//   cursor. If they cannot, display the wait cursor.
		
		// Get the pointer to the global data structures.
		threadInfo = (PPRINTTHREADINFO)GetWindowLongPtr(
						GetParent(hDlg), 
						GWLP_USERDATA);
		dlgInfo = (PPRINTDLGINFO)GetWindowLongPtr(hDlg, GWLP_USERDATA);

		if ((NULL != threadInfo) && (NULL != dlgInfo))
		{
			// If the printing thread is active, then
			//  set the main window to the normal cursor
			//  unless the print job is closing. Use 
			//  the wait cursor for the final print step.
			if (USER_PRINT_CLOSING != dlgInfo->lastPrintStatus ) 
			{
				SetCursor (threadInfo->normalCursor);
			}
			else
			{
				SetCursor (threadInfo->waitCursor);
			}
			// Indicate no further processing is required for this message.
			return (INT_PTR)TRUE;			
		}
		else
		{
			// Unable to locate the application's data structures
			//  so let the default dialog procedure handle this message.
			return (INT_PTR)FALSE;			
		}
		// Indicate no further processing is required for this message.
		return (INT_PTR)TRUE;			

	case USER_PRINT_START_PRINTING:
		// Initialize the printing structures using the information
		//  entered by the user and start the printing thread.
		{
			DWORD	stepCount = 0;
			DWORD	waitStatus = 0;

			// Get the pointer to the dialog's data structures.
			dlgInfo = (PPRINTDLGINFO)GetWindowLongPtr(hDlg, GWLP_USERDATA);

			if (NULL != dlgInfo)
			{
				// Save this step
				dlgInfo->lastPrintStatus = (DWORD)message;

				// Compute the number of steps in this job.
				stepCount = (((
							// One copy of a document contains
							//  one step for each page +
							//  one step for the document
							((threadInfo->documentContent)->pages + 1) * 
							// Each copy of the document includes
							//  two steps to open and close the document
							threadInfo->copies) + 2) * 
							// Each job includes one step to start the job
							threadInfo->packages) + 1;
				// Send the total number of steps to the progress bar control.
				SendMessage (
					dlgInfo->progressBarWindow, 
					PBM_SETRANGE32, 
					0L, 
					(stepCount));

				// Start the printing thread
				threadInfo->printThreadHandle = CreateThread (
								NULL, 
								0L, 
								(LPTHREAD_START_ROUTINE)PrintThreadProc,
								(LPVOID)threadInfo, 
								0L, 
								&threadInfo->printThreadId);

				// Make sure the printing thread started OK
				//  by waiting to see if it is still running after
				//  a short period of time. This time delay should be
				//  long enough to know that it's running but shorter
				//  than the shortest possible print job.
				waitStatus = WaitForSingleObject (
					threadInfo->printThreadHandle, 
					THREAD_START_WAIT);
				
				// If the object is signalled, that means that the
				//  thread terminated before the timeout period elapsed.
				if (WAIT_OBJECT_0 == waitStatus)
				{
					// The thread exited, so post close messages.
					PostMessage (hDlg, USER_PRINT_CLOSING, 0L, (LPARAM)E_FAIL);
					PostMessage (hDlg, USER_PRINT_COMPLETE, 0L, (LPARAM)E_FAIL);
				}
				// Indicate no further processing is required for this message.
				return (INT_PTR)TRUE;			
			}
			else
			{
				// Unable to locate dialog box data structure so send the
				//  message to the default dialog procedure.
				return (INT_PTR)FALSE;			
			}
		}

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDCANCEL:
			// Stop print thread by signalling the quit event
			SetEvent (threadInfo->quitEvent);

			// Indicate no further processing is required for this message.
			return (INT_PTR)TRUE;
		}
		// Send all other messages to the default dialog procedure.
		return (INT_PTR)FALSE;			

	case USER_PRINT_STATUS_UPDATE:
		// update the progress bar to indicate the current status
		WCHAR	statusText[MAXIMUM_RESOURCE_STRING_LENGTH];
		LPWSTR	formatString;

		// Get the pointer to the dialog's data structures.
		dlgInfo = (PPRINTDLGINFO)GetWindowLongPtr(hDlg, GWLP_USERDATA);
		if (NULL != dlgInfo)
		{
			// Save this step
			dlgInfo->lastPrintStatus = (DWORD)message;

			if (1 == threadInfo->copies)
			{
				// Display the single copy progress message

				// Get the format string for this message from the 
				//  resource file.
				GetAllocedResourceString (
					threadInfo->applicationInstance, 
					IDS_PRINTING_PAGE, 
					&formatString);

				// Format the message to show the current page.
				swprintf_s (
					statusText,
					MAXIMUM_RESOURCE_STRING_LENGTH,
					formatString,
					threadInfo->currentPage, 
					(threadInfo->documentContent)->pages);
			}
			else
			{
				// Display the multiple copy progress message

				// Get the format string for this message from the 
				//  resource file.
				GetAllocedResourceString (
					threadInfo->applicationInstance, 
					IDS_PRINTING_PAGE_AND_COPY, 
					&formatString);
		
				// Format the message to show the current page and copy.
				swprintf_s (
					statusText,
					MAXIMUM_RESOURCE_STRING_LENGTH,
					formatString,
					threadInfo->currentPage, 
					(threadInfo->documentContent)->pages,
					threadInfo->currentCopy,
					threadInfo->copies);
			}
			// Update the status text
			SetDlgItemText(hDlg, IDC_PRINT_STATUS, statusText);

			// Free the format string
			if (NULL != formatString) 
			{
				HeapFree (GetProcessHeap(), 0L, (LPVOID)formatString);
			}
			
			// Update the progress bar status and redraw the progress bar
			PostMessage (dlgInfo->progressBarWindow, PBM_DELTAPOS, 1L, 0L);
			PostMessage (dlgInfo->progressBarWindow, WM_PAINT, 0L, 0L);

			// Indicate no further processing is required for this message.
			return (INT_PTR)TRUE;			
		}
		else
		{
			// Unable to locate dialog box data structure so send the
			//  message to the default dialog procedure.
			return (INT_PTR)FALSE;			
		}

	case USER_PRINT_CLOSING:
		{
			LPWSTR	messageString = NULL;
			int maxRange = 0;

			// Get the pointer to the dialog's data structures.
			dlgInfo = (PPRINTDLGINFO)GetWindowLongPtr(hDlg, GWLP_USERDATA);
			if (NULL != dlgInfo)
			{
				// save this step
				dlgInfo->lastPrintStatus = (DWORD)message;
				// Max out the progress bar in case it was in the middle.
				//  For example, if the job was cancelled.
				maxRange = SendMessage (
					dlgInfo->progressBarWindow, 
					PBM_GETRANGE, 
					(WPARAM)FALSE, 
					(LPARAM)0L);
				PostMessage (
					dlgInfo->progressBarWindow, 
					PBM_SETPOS,
					maxRange, 
					0L);
				// Send a paint message to redraw the updated progress bar.
				PostMessage (
					dlgInfo->progressBarWindow, 
					WM_PAINT, 
					0L, 
					0L);

				// Disable Cancel button because there's no 
				// way to cancel at this point.
				EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);

				// Display the status message that reflects the
				//  reason the print job is closing.
				if (lParam == ERROR_SUCCESS)
				{
					// Closing can take some time so inform the user
					GetAllocedResourceString (
						threadInfo->applicationInstance, 
						IDS_FINISHING_JOB, 
						&messageString);
				}
				else
				{
					// Cancelling can take some time so inform the user
					GetAllocedResourceString (
						threadInfo->applicationInstance, 
						IDS_CANCELLING_JOB, 
						&messageString);
				}
				// Set message text
				SetDlgItemText(hDlg, IDC_PRINT_STATUS, messageString);

				// Force redraw of new status text
				InvalidateRect(
					GetDlgItem(hDlg,IDC_PRINT_STATUS), 
					NULL, 
					FALSE);
				SendMessage(
					GetDlgItem(hDlg,IDC_PRINT_STATUS), 
					WM_PAINT, 
					(WPARAM)0L, 
					(LPARAM)0L);

				if (NULL != messageString)
				{
					HeapFree (
						GetProcessHeap(), 
						0L, 
						(LPVOID)messageString);
				}

				// Indicate no further processing is 
				//  required for this message.
				return (INT_PTR)TRUE;			
			}
			else
			{
				// Unable to locate dialog box data structure so send the
				//  message to the default dialog procedure.
				return (INT_PTR)FALSE;			
			}
		}

	case USER_PRINT_COMPLETE:
		// Print job has finished so end clean up and close the dialog.

		// Get the pointer to the dialog's data structures.
		dlgInfo = (PPRINTDLGINFO)GetWindowLongPtr(hDlg, GWLP_USERDATA);

		// Clear the status field
		SetDlgItemText(hDlg, IDC_PRINT_STATUS, L"");

		// Display the message box that indicates how the print
		//  job ended.
		if (ERROR_SUCCESS == lParam)
		{
			// Show success message to user.
			MessageBoxWithResourceString (threadInfo->applicationInstance,
				hDlg, IDS_SUCCESS_FINISHED, IDS_CAPTION_SUCCESS,
				(UINT)(MB_OK | MB_ICONINFORMATION));
		}
		else
		{
			// Show canceleed message to user.
			MessageBoxWithResourceString (threadInfo->applicationInstance,
				hDlg, IDS_INFO_CANCELLED, IDS_CAPTION_INFO,
				(UINT)(MB_OK | MB_ICONINFORMATION));
		}

		// Make sure thread has exited
		if (NULL != threadInfo->printThreadHandle)
		{
			waitStatus = WaitForSingleObject (
				threadInfo->printThreadHandle, 
				THREAD_END_WAIT);

			// It doesn't matter what the wait status was.
			//  Close all the handles anyway.
			CloseHandle (threadInfo->printThreadHandle);
			threadInfo->printThreadHandle = NULL;
		}

		if (NULL != threadInfo->quitEvent)
		{
			CloseHandle (threadInfo->quitEvent);
			threadInfo->quitEvent = NULL;
		}

		// release the thread data
		if (NULL != dlgInfo)
		{
			// save this step
			HeapFree (GetProcessHeap(), 0L, dlgInfo);
		}

		// release devmode structure
		if (NULL != threadInfo->devmode) {
			HeapFree (
				GetProcessHeap(), 
				0L, 
				threadInfo->devmode);
			threadInfo->devmode = NULL;
		}

		// close this dialog box
		EndDialog(hDlg, LOWORD(wParam));
		return (INT_PTR)TRUE;			

	default:
		// Let the default Dlg proc handle all other messages.
		return (INT_PTR)FALSE;
	}
}

